﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using SZORM;
using SZORM.Core.Visitors;
using SZORM.DbExpressions;
using SZORM.Descriptors;
using SZORM.Extensions;
using SZORM.Infrastructure;
using SZORM.Query;
using SZORM.Utility;
using SZORM.Utility.SnowId;

namespace SZORM
{
    /// <summary>
    /// 提供对实体类型的数据库操作集合，包括查询、添加、编辑和删除功能
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    public class DbSet<TEntity> : IDbSet<TEntity>, ISZORM<TEntity> where TEntity : class
    {
        /// <summary>
        /// 获取或设置当前数据库上下文实例
        /// </summary>
        public DbContext DbContext
        {
            get; set;
        }
        /// <summary>
        /// 查询必须使用该方法
        /// </summary>
        /// <returns></returns>
        /// <summary>
        /// 创建实体查询对象
        /// </summary>
        /// <returns>查询对象</returns>
        public IQuery<TEntity> AsQuery()
        {
            return AsQuery(null);
        }
        /// <summary>
        /// 创建指定表名的实体查询对象
        /// </summary>
        /// <param name="table">表名</param>
        /// <returns>查询对象</returns>
        public IQuery<TEntity> AsQuery(string table)
        {
            return new Query<TEntity>(this.DbContext, table);
        }
        private static bool IsEmptyValue(object value)
        {
            if (value == null)
                return true;

            if (value is string s)
                return string.IsNullOrEmpty(s);

            if (value is long && (long)value== default(long))
                return true;

            // 可以继续添加其它类型的判断逻辑

            return false;
        }
        /// <summary>
        /// 增加
        /// </summary>
        /// <param name="body">() => new User() { Name = "lu", Age = 18, Gender = Gender.Man, CityId = 1, OpTime = DateTime.Now }</param>
        /// <returns></returns>
        public string Add(Expression<Func<TEntity>> body)
        {
            var (key, sql, parameters) = BuildInsertSqlFromExpression(body);
            this.DbContext.ExecuteNoQuery(sql, parameters);
            return key.ToString();
        }
        public async Task<string> AddAsync(Expression<Func<TEntity>> body)
        {
            var (key, sql, parameters) = BuildInsertSqlFromExpression(body);
            await this.DbContext.ExecuteNoQueryAsync(sql, parameters);
            return key.ToString();
        }
        //private string BuildAddSqlFromExpression()
        private (object key,string Sql, DbParam[] Parameters) BuildInsertSqlFromExpression(Expression<Func<TEntity>> body)
        {
            Checks.NotNull(body, "body");

            TypeDescriptor typeDescriptor = TypeDescriptor.GetDescriptor(typeof(TEntity));

            Dictionary<MemberInfo, Expression> insertColumns = InitMemberExtractor.Extract(body);

            DbInsertExpression e = new(typeDescriptor.Table);

            object keyVal = null;

            foreach (var kv in insertColumns)
            {
                MemberInfo key = kv.Key;
                MappingMemberDescriptor memberDescriptor = typeDescriptor.TryGetMappingMemberDescriptor(key);
                object val = ExpressionEvaluator.Evaluate(kv.Value);

                // 主键处理
                if (memberDescriptor.SZColumnAttribute.IsKey)
                {
                    if (IsEmptyValue(val))
                    {
                        if (memberDescriptor.MemberInfoType == typeof(string))
                        {
                            val = GetGuidKey();
                        }
                        else if (memberDescriptor.MemberInfoType == typeof(long))
                        {
                            val = GetSnowIdKey();
                        }
                    }
                    keyVal = val;
                    e.InsertColumns.Add(memberDescriptor.Column, DbExpression.Parameter(val));
                    continue;
                }

                // 时间字段处理
                if (memberDescriptor.SZColumnAttribute.IsAddTime || memberDescriptor.SZColumnAttribute.IsEditTime)
                {
                    val = DateTime.Now;
                    e.InsertColumns.Add(memberDescriptor.Column, DbExpression.Parameter(val));
                    continue;
                }

                DbExpression valExp = DbExpression.Parameter(val, memberDescriptor.MemberInfoType);
                e.InsertColumns.Add(memberDescriptor.Column, valExp);
            }

            IDbExpressionTranslator translator = this.DbContext.DatabaseProvider.CreateDbExpressionTranslator();
            string sql = translator.Translate(e, out List<DbParam> parameters);

            return (keyVal,sql, parameters.ToArray());
        }
        /// <summary>
        /// 添加实体到数据库
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <returns>添加后的实体</returns>
        public TEntity Add(TEntity entity)
        {
            var (result, sql, parameters) = BuildInsertSqlFromEntity(entity);

            this.DbContext.ExecuteNoQuery(sql, parameters);

            return result;
        }
        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public async Task<TEntity> AddAsync(TEntity entity)
        {
            var (result, sql, parameters) = BuildInsertSqlFromEntity(entity);

            await this.DbContext.ExecuteNoQueryAsync(sql, parameters);

            return result;
        }

        private (TEntity result, string Sql, DbParam[] Parameters) BuildInsertSqlFromEntity(TEntity entity)
        {
            Checks.NotNull(entity, nameof(entity));

            TypeDescriptor typeDescriptor = TypeDescriptor.GetDescriptor(entity.GetType());

            Dictionary<MappingMemberDescriptor, DbExpression> insertColumns = [];
            foreach (var kv in typeDescriptor.MappingMemberDescriptors)
            {
                MappingMemberDescriptor memberDescriptor = kv.Value;

                object val = memberDescriptor.GetValue(entity);

                if (memberDescriptor.SZColumnAttribute.IsKey)
                {
                    if (IsEmptyValue(val))
                    {
                        if (memberDescriptor.MemberInfoType == typeof(string))
                        {
                            val = GetGuidKey();
                        }
                        else if (memberDescriptor.MemberInfoType == typeof(long))
                        {
                            val = GetSnowIdKey();
                        }
                        memberDescriptor.SetValue(entity, val);
                    }
                }
                if (memberDescriptor.SZColumnAttribute.IsAddTime || memberDescriptor.SZColumnAttribute.IsEditTime)
                {
                    val = DateTime.Now;
                    memberDescriptor.SetValue(entity, val);
                }
                DbExpression valExp = DbExpression.Parameter(val, memberDescriptor.MemberInfoType);
                insertColumns.Add(memberDescriptor, valExp);
            }

            DbInsertExpression e = new DbInsertExpression(typeDescriptor.Table);

            foreach (var kv in insertColumns)
            {
                e.InsertColumns.Add(kv.Key.Column, kv.Value);
            }
            IDbExpressionTranslator translator = this.DbContext.DatabaseProvider.CreateDbExpressionTranslator();
            string sql = translator.Translate(e, out List<DbParam> parameters);
            return (entity, sql, parameters.ToArray());
        }
        /// <inheritdoc/>
        public TEntity Edit(TEntity entity)
        {
            var (result, sql, parameters) = BuildUpdataSqlFromEntity(entity);
            this.DbContext.ExecuteNoQuery(sql, parameters);

            return result;
        }
        /// <inheritdoc/>
        public async Task<TEntity> EditAsync(TEntity entity)
        {
            var (result, sql, parameters) = BuildUpdataSqlFromEntity(entity);
            await this.DbContext.ExecuteNoQueryAsync(sql, parameters);

            return result;
        }
        private (TEntity result, string Sql, DbParam[] Parameters) BuildUpdataSqlFromEntity(TEntity entity)
        {
            Checks.NotNull(entity, nameof(entity));

            TypeDescriptor typeDescriptor = TypeDescriptor.GetDescriptor(entity.GetType());

            object keyVal = null;
            MappingMemberDescriptor keyMemberDescriptor = null;

            Dictionary<MappingMemberDescriptor, DbExpression> updateColumns = [];
            foreach (var kv in typeDescriptor.MappingMemberDescriptors)
            {
                MemberInfo member = kv.Key;
                MappingMemberDescriptor memberDescriptor = kv.Value;

                object val = memberDescriptor.GetValue(entity);
                if (memberDescriptor.SZColumnAttribute.IsKey)
                {
                    if (val == null)
                        throw new SZORMException("主键值不允许为空.");
                    keyVal = val;
                    keyMemberDescriptor = memberDescriptor;
                    continue;
                }

                if (memberDescriptor.SZColumnAttribute.IsEditTime)
                {
                    val = DateTime.Now;
                    memberDescriptor.SetValue(entity, val);
                }


                DbExpression valExp = DbExpression.Parameter(val, memberDescriptor.MemberInfoType);
                updateColumns.Add(memberDescriptor, valExp);
            }

            DbExpression left = new DbColumnAccessExpression(typeDescriptor.Table, keyMemberDescriptor.Column);
            DbExpression right = DbExpression.Parameter(keyVal, keyMemberDescriptor.MemberInfoType);
            DbExpression conditionExp = new DbEqualExpression(left, right);

            DbUpdateExpression e = new DbUpdateExpression(typeDescriptor.Table, conditionExp);

            foreach (var item in updateColumns)
            {
                e.UpdateColumns.Add(item.Key.Column, item.Value);
            }

            IDbExpressionTranslator translator = this.DbContext.DatabaseProvider.CreateDbExpressionTranslator();
            string sql = translator.Translate(e, out List<DbParam> parameters);
            return (entity, sql, parameters.ToArray());
        }
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="condition">a => a.Id == 1</param>
        /// <param name="body">a => new User() { Name = a.Name, Age = a.Age + 100, Gender = Gender.Man, OpTime = DateTime.Now }</param>
        /// <returns></returns>
        public int Edit(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, TEntity>> body)
        {
            var (sql, parameters) = BuildUpdataSqlFromExpression(condition, body);
            return this.DbContext.ExecuteNoQuery(sql, parameters);
        }
        /// <inheritdoc/>
        public async Task<int> EditAsync(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, TEntity>> body)
        {
            var (sql, parameters) = BuildUpdataSqlFromExpression(condition, body);
            return await this.DbContext.ExecuteNoQueryAsync(sql, parameters);
        }
        private (string Sql, DbParam[] Parameters) BuildUpdataSqlFromExpression(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, TEntity>> body)
        {
            Checks.NotNull(body, "body");

            TypeDescriptor typeDescriptor = TypeDescriptor.GetDescriptor(typeof(TEntity));

            Dictionary<MemberInfo, Expression> updateColumns = InitMemberExtractor.Extract(body);

            DefaultExpressionParser expressionParser = typeDescriptor.GetExpressionParser(null);
            DbExpression conditionExp = expressionParser.ParseFilterPredicate(condition);
            DbUpdateExpression e = new DbUpdateExpression(typeDescriptor.Table, conditionExp);

            foreach (var kv in updateColumns)
            {
                MemberInfo key = kv.Key;
                MappingMemberDescriptor memberDescriptor = typeDescriptor.TryGetMappingMemberDescriptor(key);

                e.UpdateColumns.Add(memberDescriptor.Column, expressionParser.Parse(kv.Value));
            }

            IDbExpressionTranslator translator = this.DbContext.DatabaseProvider.CreateDbExpressionTranslator();
            string sql = translator.Translate(e, out List<DbParam> parameters);
            return (sql, parameters.ToArray());
        }
        /// <summary>
        /// 根据主键查找实体
        /// </summary>
        /// <param name="keyValue">主键值</param>
        /// <returns>实体对象</returns>
        public TEntity Find(object keyValue)
        {
            Expression<Func<TEntity, bool>> predicate = PredicateBuilds.BuildPredicate<TEntity>(keyValue);
            var q = new Query<TEntity>(this.DbContext).Where(predicate);

            return q.FirstOrDefault();
        }
        /// <summary>
        /// 异步根据主键查找实体
        /// </summary>
        /// <param name="keyValue">主键值</param>
        /// <returns>包含实体对象的任务</returns>
        public async Task<TEntity> FindAsync(object keyValue)
        {
            Expression<Func<TEntity, bool>> predicate = PredicateBuilds.BuildPredicate<TEntity>(keyValue);
            var q = new Query<TEntity>(this.DbContext).Where(predicate);

            return await q.FirstOrDefaultAsync();
        }
        /// <summary>
        /// 根据主键删除实体
        /// </summary>
        /// <param name="keyValue">主键值</param>
        /// <returns>受影响的行数</returns>
        public int Remove(object keyValue)
        {
            Expression<Func<TEntity, bool>> predicate = PredicateBuilds.BuildPredicate<TEntity>(keyValue);
            return Remove(predicate);
        }
        /// <inheritdoc/>
        public Task<int> RemoveAsync(object keyValue)
        {
            Expression<Func<TEntity, bool>> predicate = PredicateBuilds.BuildPredicate<TEntity>(keyValue);
            return RemoveAsync(predicate);
        }
        /// <summary>
        /// 根据条件删除实体
        /// </summary>
        /// <param name="condition">删除条件</param>
        /// <returns>受影响的行数</returns>
        public int Remove(Expression<Func<TEntity, bool>> condition)
        {
            var (sql, parameters)=BuildDeleteSqlFromExpression(condition);
            return this.DbContext.ExecuteNoQuery(sql, parameters);
        }
        /// <summary>
        /// 根据条件删除实体
        /// </summary>
        public async Task<int> RemoveAsync(Expression<Func<TEntity, bool>> condition)
        {
            var (sql, parameters) = BuildDeleteSqlFromExpression(condition);
            return await this.DbContext.ExecuteNoQueryAsync(sql, parameters);
        }
        private (string Sql, DbParam[] Parameters) BuildDeleteSqlFromExpression(Expression<Func<TEntity, bool>> condition)
        {
            Checks.NotNull(condition, nameof(condition));

            TypeDescriptor typeDescriptor = TypeDescriptor.GetDescriptor(typeof(TEntity));
            DefaultExpressionParser expressionParser = typeDescriptor.GetExpressionParser(null);
            DbExpression conditionExp = expressionParser.ParseFilterPredicate(condition);

            DbDeleteExpression e = new(typeDescriptor.Table, conditionExp);

            IDbExpressionTranslator translator = this.DbContext.DatabaseProvider.CreateDbExpressionTranslator();
            string sql = translator.Translate(e, out List<DbParam> parameters);
            return (sql, parameters.ToArray());
        }
        /// <summary>
        /// 删除指定实体
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <returns>受影响的行数</returns>
        public bool Remove(TEntity entity)
        {
            var (sql, parameters) = BuildDeleteSqlFromEntity(entity);

            return this.DbContext.ExecuteNoQuery(sql, parameters) == 1;
        }
        /// <inheritdoc/>
        public async Task<bool> RemoveAsync(TEntity entity)
        {
            var (sql, parameters) = BuildDeleteSqlFromEntity(entity);

            return await this.DbContext.ExecuteNoQueryAsync(sql, parameters) == 1;
        }
        private (string Sql, DbParam[] Parameters) BuildDeleteSqlFromEntity(TEntity entity)
        {
            Checks.NotNull(entity, nameof(entity));

            TypeDescriptor typeDescriptor = TypeDescriptor.GetDescriptor(entity.GetType());

            MappingMemberDescriptor keyMemberDescriptor = typeDescriptor.PrimaryKey;
            MemberInfo keyMember = typeDescriptor.PrimaryKey.MemberInfo;
            if (keyMemberDescriptor == null)
            {
                throw new SZORMException(string.Format("该表没有主键不允许这样删除."));
            }

            var keyVal = keyMemberDescriptor.GetValue(entity);

            if (keyVal == null)
                throw new SZORMException(string.Format("{0}主键字段不允许为空.", keyMember.Name));

            DbExpression left = new DbColumnAccessExpression(typeDescriptor.Table, keyMemberDescriptor.Column);
            DbExpression right = new DbParameterExpression(keyVal);
            DbExpression conditionExp = new DbEqualExpression(left, right);

            DbDeleteExpression e = new DbDeleteExpression(typeDescriptor.Table, conditionExp);
            IDbExpressionTranslator translator = this.DbContext.DatabaseProvider.CreateDbExpressionTranslator();
            string sql = translator.Translate(e, out List<DbParam> parameters);
            return (sql, parameters.ToArray());
        }
        /// <summary>
        /// 生成雪花ID作为主键值
        /// </summary>
        /// <returns>雪花ID</returns>
        private static long GetSnowIdKey()
        {
            return SnowIdHelper.NextId();
        }
        /// <summary>
        /// 生成GUID作为主键值（去除连字符）
        /// </summary>
        /// <returns>格式化后的GUID字符串</returns>
        private static string GetGuidKey()
        {
            return Guid.NewGuid().ToString().Replace("-", "");
        }
    }
}
